
#ifndef ROPUFU_MULTI_SENSOR_KNOWN_THRESHOLDS_HPP_INCLUDED
#define ROPUFU_MULTI_SENSOR_KNOWN_THRESHOLDS_HPP_INCLUDED

#include "information/information_none.hpp"
#include "information/information_exact.hpp"
#include "information/information_at_most.hpp"

#include "statistic/bayes_cusum.hpp"
#include "statistic/generalized_cusum.hpp"
#include "statistic/oracle_cusum.hpp"
#include "statistic/ordered_sum_cusum.hpp"
#include "statistic/parallel_max_cusum.hpp"
#include "statistic/parallel_mix_cusum.hpp"

#include "window/window_all_cusum.hpp"
#include "window/window_fixed.hpp"
#include "window/window_none.hpp"
#include "window/window_rlr.hpp"

#include <cstddef>
#include <vector>

namespace ropufu
{
    namespace detail
    {
        template <typename t_statistic_type>
        struct known_thresholds_list
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                return false;
            }
        };

        // ~~ Oracle CUSUM ~~

        template <>
        struct known_thresholds_list<statistic::oracle_cusum<5, 5>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.4e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.800, 3.100, 9.870, 10.05 };
                    return true;
                }
                return false;
            }
        };

        template <>
        struct known_thresholds_list<statistic::oracle_cusum<5, 4>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.4e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.900, 3.150, 9.870, 10.05 };
                    return true;
                }
                return false;
            }
        };

        template <>
        struct known_thresholds_list<statistic::oracle_cusum<5, 3>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.1e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 3.000, 3.200, 9.880, 10.05 };
                    return true;
                }
                return false;
            }
        };

        template <>
        struct known_thresholds_list<statistic::oracle_cusum<5, 2>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.900, 3.100, 9.850, 10.00 };
                    return true;
                }
                return false;
            }
        };

        template <>
        struct known_thresholds_list<statistic::oracle_cusum<5, 1>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.3e4, 1.1e5 };
                    thresholds = { 2.800, 2.880, 9.620, 9.750 };
                    return true;
                }
                return false;
            }
        };

        // ~~ Bayes CUSUM ~~

        template <typename t_window_type>
        struct known_thresholds_list<statistic::bayes_cusum<5, information_none<5>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.4e1, 1.1e2, 9.2e4, 1.1e5 };
                    thresholds = { 2.880, 3.000, 9.750, 9.900 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::bayes_cusum<5, information_exact<5, 4>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.2e4, 1.1e5 };
                    thresholds = { 2.900, 3.130, 9.820, 10.00 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::bayes_cusum<5, information_exact<5, 3>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.2e4, 1.1e5 };
                    thresholds = { 2.850, 3.120, 9.820, 10.00 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::bayes_cusum<5, information_exact<5, 2>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.2e4, 1.1e5 };
                    thresholds = { 2.800, 2.950, 9.700, 9.900 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::bayes_cusum<5, information_at_most<5, 4>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.4e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.800, 2.950, 9.650, 9.900 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::bayes_cusum<5, information_at_most<5, 3>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.4e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.600, 2.750, 9.550, 9.800 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::bayes_cusum<5, information_at_most<5, 2>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.400, 2.600, 9.300, 9.450 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::bayes_cusum<5, information_at_most<5, 1>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 1.930, 2.100, 8.750, 8.900 };
                    return true;
                }
                return false;
            }
        };

        // ~~ Mix-CUSUM ~~

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_mix_cusum<5, information_none<5>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.7e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 3.100, 3.200, 9.860, 10.00 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_mix_cusum<5, information_exact<5, 4>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.7e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 3.000, 3.250, 9.870, 10.10 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_mix_cusum<5, information_exact<5, 3>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.7e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 3.120, 3.250, 9.900, 10.10 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_mix_cusum<5, information_exact<5, 2>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.7e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 3.050, 3.200, 9.870, 9.980 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_mix_cusum<5, information_at_most<5, 4>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.7e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 3.000, 3.200, 9.870, 10.05 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_mix_cusum<5, information_at_most<5, 3>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.7e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 3.000, 3.250, 9.800, 10.00 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_mix_cusum<5, information_at_most<5, 2>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.7e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.900, 3.130, 9.780, 9.950 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_mix_cusum<5, information_at_most<5, 1>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.7e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.800, 2.900, 9.600, 9.750 };
                    return true;
                }
                return false;
            }
        };

        // ~~ Max-CUSUM ~~

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_max_cusum<5, information_none<5>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.000, 2.100, 9.500, 9.650 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_max_cusum<5, information_exact<5, 4>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.500, 2.750, 9.750, 9.900 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_max_cusum<5, information_exact<5, 3>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.500, 2.700, 9.750, 9.920 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_max_cusum<5, information_exact<5, 2>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.650, 2.800, 9.800, 9.950 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_max_cusum<5, information_at_most<5, 4>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 1.950, 2.200, 9.500, 9.700 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_max_cusum<5, information_at_most<5, 3>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.100, 2.300, 9.600, 9.750 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_max_cusum<5, information_at_most<5, 2>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.420, 2.550, 9.700, 9.875 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_max_cusum<5, information_at_most<5, 1>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 2.680, 2.800, 9.620, 9.750 };
                    return true;
                }
                return false;
            }
        };

        // ~~ Mei ~~

        template <typename t_window_type>
        struct known_thresholds_list<statistic::ordered_sum_cusum<5, information_none<5>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.4e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 6.700, 6.900, 16.80, 17.10 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type, std::size_t t_count_streams, std::size_t t_count_affected>
        struct known_thresholds_list<statistic::ordered_sum_cusum<5, information_exact<t_count_streams, t_count_affected>, t_window_type>>
            : public known_thresholds_list<statistic::ordered_sum_cusum<5, information_at_most<t_count_streams, t_count_affected>, t_window_type>>
        {
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::ordered_sum_cusum<5, information_at_most<5, 4>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 7.200, 7.400, 16.70, 16.90 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::ordered_sum_cusum<5, information_at_most<5, 3>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 6.850, 7.300, 15.80, 16.10 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::ordered_sum_cusum<5, information_at_most<5, 2>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 5.950, 6.250, 14.20, 14.30 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::ordered_sum_cusum<5, information_at_most<5, 1>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 4.300, 4.400, 11.20, 11.37 };
                    return true;
                }
                return false;
            }
        };

        // ~~ 10 sensors ~~

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_max_cusum<10, information_none<10>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.8e1, 1.1e2, 9.5e4, 1.1e5 };
                    thresholds = { 0.685, 0.710, 8.750, 8.850 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_max_cusum<10, information_at_most<10, 7>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.5e1, 1.1e2, 8.0e4, 1.1e5 };
                    thresholds = { 0.720, 0.820, 6.200, 6.500 };
                    return true;
                }
                return false;
            }
        };

        template <typename t_window_type>
        struct known_thresholds_list<statistic::parallel_max_cusum<10, information_at_most<10, 5>, t_window_type>>
        {
            static bool populate(double theta, std::vector<double>& arls, std::vector<double>& thresholds)
            {
                if (theta == 1.0)
                {
                    arls       = { 9.4e1, 1.1e2, 9.0e4, 1.2e5 };
                    thresholds = { 1.010, 1.150, 6.700, 6.850 };
                    return true;
                }
                return false;
            }
        };
    }

    struct known_thresholds
    {
        template <typename t_statistic_type>
        static bool for_theta_and_arl(const t_statistic_type& proc, double theta, double arl,
            double& low_threshold, double& high_threshold)
        {
            std::vector<double> arls;
            std::vector<double> thresholds;
            if (!detail::known_thresholds_list<t_statistic_type>::populate(theta, arls, thresholds)) return false;

            bool is_low_known = false;
            bool is_high_known = false;
            // Warning: ARL's have to be sorted in increasing order.
            for (std::size_t i = 0; i < arls.size(); i++)
            {
                if (arls[i] >= arl) break;
                low_threshold = thresholds[i];
                is_low_known = true;
            }
            for (std::size_t i = arls.size() - 1; i >= 0; i--)
            {
                if (arls[i] <= arl) break;
                high_threshold = thresholds[i];
                is_high_known = true;
            }

            return is_low_known && is_high_known;
        }
    };
}

#endif // ROPUFU_MULTI_SENSOR_KNOWN_THRESHOLDS_HPP_INCLUDED
